package com.dingzhuo.energy.common.utils.file;

import java.io.File;
import java.io.IOException;

import com.dingzhuo.energy.common.constant.Constants;
import com.dingzhuo.energy.common.exception.file.FileNameLengthLimitExceededException;
import com.dingzhuo.energy.common.exception.file.FileSizeLimitExceededException;
import com.dingzhuo.energy.common.exception.file.InvalidExtensionException;
import com.dingzhuo.energy.common.utils.security.Md5Utils;
import com.dingzhuo.energy.framework.config.RuoYiConfig;
import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile;
import com.dingzhuo.energy.common.constant.Constants;
import com.dingzhuo.energy.common.exception.file.FileNameLengthLimitExceededException;
import com.dingzhuo.energy.common.exception.file.FileSizeLimitExceededException;
import com.dingzhuo.energy.common.exception.file.InvalidExtensionException;
import com.dingzhuo.energy.common.utils.DateUtils;
import com.dingzhuo.energy.common.utils.StringUtils;
import com.dingzhuo.energy.common.utils.security.Md5Utils;
import com.dingzhuo.energy.framework.config.RuoYiConfig;

/**
 * 文件上传工具类
 *
 * @author ruoyi
 */
public class FileUploadUtils {

  /**
   * 默认大小 50M
   */
  public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;

  /**
   * 默认的文件名最大长度 100
   */
  public static final int DEFAULT_FILE_NAME_LENGTH = 100;

  /**
   * 默认上传的地址
   */
  private static String defaultBaseDir = RuoYiConfig.getProfile();

  private static int counter = 0;

  public static void setDefaultBaseDir(String defaultBaseDir) {
    FileUploadUtils.defaultBaseDir = defaultBaseDir;
  }

  public static String getDefaultBaseDir() {
    return defaultBaseDir;
  }

  /**
   * 以默认配置进行文件上传
   *
   * @param file 上传的文件
   * @return 文件名称
   * @throws Exception
   */
  public static final String upload(MultipartFile file) throws IOException {
    try {
      return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
    } catch (Exception e) {
      throw new IOException(e.getMessage(), e);
    }
  }

  /**
   * 根据文件路径上传
   *
   * @param baseDir 相对应用的基目录
   * @param file    上传的文件
   * @return 文件名称
   * @throws IOException
   */
  public static final String upload(String baseDir, MultipartFile file) throws IOException {
    try {
      return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
    } catch (Exception e) {
      throw new IOException(e.getMessage(), e);
    }
  }

  /**
   * 根据文件路径上传任意文件 2020.02.15
   *
   * @param baseDir 相对应用的基目录
   * @param file    上传的文件
   * @return 文件名称
   * @throws IOException
   */
  public static final String uploadAll(String baseDir, MultipartFile file) throws IOException {
    try {
      return uploadAllFile(baseDir, file);
    } catch (Exception e) {
      throw new IOException(e.getMessage(), e);
    }
  }

  /**
   * 任意文件上传 2020.02.15
   *
   * @param baseDir 相对应用的基目录
   * @param file    上传的文件
   * @return 返回上传成功的文件名
   * @throws FileSizeLimitExceededException       如果超出最大大小
   * @throws FileNameLengthLimitExceededException 文件名太长
   * @throws IOException                          比如读写文件出错时
   * @throws InvalidExtensionException            文件校验异常
   */
  public static final String uploadAllFile(String baseDir, MultipartFile file)
      throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
      InvalidExtensionException {
    int fileNamelength = file.getOriginalFilename().length();
    if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
      throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
    }
    //检测任意文件类型大小，不检测文件类型
    assertAllowedNoExtension(file);
    //生成日期路径的文件及新文件命名编码，避免覆盖
    String fileName = extractFilename(file);
    //检测文件路径及文件并对 没有的路径和文件进行建立
    getAbsoluteFilePath(baseDir + File.separator + fileName);
    String saveFilePath = baseDir + File.separator + fileName;
    File desc = new File(saveFilePath);
    //将上传文件写入新文件
    file.transferTo(desc);
    //返回全路径
    return saveFilePath;
  }

  /**
   * 文件上传
   *
   * @param baseDir          相对应用的基目录
   * @param file             上传的文件
   * @param allowedExtension 上传文件类型
   * @return 返回上传成功的文件名
   * @throws FileSizeLimitExceededException       如果超出最大大小
   * @throws FileNameLengthLimitExceededException 文件名太长
   * @throws IOException                          比如读写文件出错时
   * @throws InvalidExtensionException            文件校验异常
   */
  public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
      throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
      InvalidExtensionException {
    int fileNamelength = file.getOriginalFilename().length();
    if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
      throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
    }

    assertAllowed(file, allowedExtension);

    String fileName = extractFilename(file);

    File desc = getAbsoluteFile(baseDir, fileName);
    file.transferTo(desc);
    String pathFileName = getPathFileName(baseDir, fileName);
    return pathFileName;
  }

  /**
   * 编码文件名
   */
  public static final String extractFilename(MultipartFile file) {
    String fileName = file.getOriginalFilename();
    String extension = getExtension(file);
    fileName = DateUtils.datePath() + "/" + encodingFilename(fileName) + "." + extension;
    return fileName;
  }

  private static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException {
    File desc = new File(uploadDir + File.separator + fileName);

    if (!desc.getParentFile().exists()) {
      desc.getParentFile().mkdirs();
    }

    return desc;
  }

  /**
   * 判断路径是否存在,不存在则新建
   *
   * @param uploadDir
   * @return
   * @throws IOException
   */
  public static final void getAbsoluteFilePath(String uploadDir) throws IOException {
    File desc = new File(uploadDir);
    if (!desc.getParentFile().exists()) {
      desc.getParentFile().mkdirs();
    }
  }

  private static final String getPathFileName(String uploadDir, String fileName)
      throws IOException {
    int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
    String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
    String pathFileName = Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
    return pathFileName;
  }

  /**
   * 编码文件名
   */
  private static final String encodingFilename(String fileName) {
    fileName = fileName.replace("_", " ");
    fileName = Md5Utils.hash(fileName + System.nanoTime() + counter++);
    return fileName;
  }

  /**
   * 文件大小校验
   *
   * @param file 上传的文件
   * @return
   * @throws FileSizeLimitExceededException 如果超出最大大小
   * @throws InvalidExtensionException
   */
  public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
      throws FileSizeLimitExceededException, InvalidExtensionException {
    long size = file.getSize();
    if (DEFAULT_MAX_SIZE != -1 && size > DEFAULT_MAX_SIZE) {
      throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
    }

    String fileName = file.getOriginalFilename();
    String extension = getExtension(file);
    if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) {
      if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) {
        throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension,
            extension,
            fileName);
      } else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) {
        throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension,
            extension,
            fileName);
      } else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) {
        throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension,
            extension,
            fileName);
      } else {
        throw new InvalidExtensionException(allowedExtension, extension, fileName);
      }
    }

  }

  /**
   * 文件大小校验 不检测类型
   *
   * @param file 上传的文件
   * @return
   * @throws FileSizeLimitExceededException 如果超出最大大小
   */
  public static final void assertAllowedNoExtension(MultipartFile file)
      throws FileSizeLimitExceededException {
    long size = file.getSize();
    if (DEFAULT_MAX_SIZE != -1 && size > DEFAULT_MAX_SIZE) {
      throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
    }
  }

  /**
   * 判断MIME类型是否是允许的MIME类型
   *
   * @param extension
   * @param allowedExtension
   * @return
   */
  public static final boolean isAllowedExtension(String extension, String[] allowedExtension) {
    for (String str : allowedExtension) {
      if (str.equalsIgnoreCase(extension)) {
        return true;
      }
    }
    return false;
  }

  /**
   * 获取文件名的后缀
   *
   * @param file 表单文件
   * @return 后缀名
   */
  public static final String getExtension(MultipartFile file) {
    String extension = FilenameUtils.getExtension(file.getOriginalFilename());
    if (StringUtils.isEmpty(extension)) {
      extension = MimeTypeUtils.getExtension(file.getContentType());
    }
    return extension;
  }
}
